package com.lboyang.provider.registry.zk;

import com.lboyang.common.dto.NrpcRequest;
import com.lboyang.common.enums.RpcErrorMessageEnum;
import com.lboyang.common.exception.RpcException;
import com.lboyang.common.extension.ExtensionLoader;
import com.lboyang.provider.loadbalance.LoadBalance;
import com.lboyang.provider.registry.ServiceDiscovery;
import com.lboyang.provider.registry.zk.util.CuratorUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;

import java.net.InetSocketAddress;
import java.util.List;

/**
 * @Description
 *      基于 ZooKeeper 实现服务发现
 * @Date 2021/9/11 22:58
 * @author: A.iguodala
 */
@Slf4j
public class ZkServiceDiscoveryImpl implements ServiceDiscovery {

    /**
     * 负载均衡策略
     */
    private final LoadBalance loadBalance;

    /**
     * SPI 获取负载均衡策略
     */
    public ZkServiceDiscoveryImpl() {
        this.loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension("loadBalance");
    }

    /**
     * 通过服务名发现服务，返回负载均衡后选择的真正服务提供者地址
     * @param request
     * @return
     */
    @Override
    public InetSocketAddress lookupService(NrpcRequest request) {
        // 获取服务名
        String rpcServiceName = request.getRpcServiceName();
        // 获取 zk 客户端框架
        CuratorFramework zkClient = CuratorUtils.getZkClient();
        // 获取该服务下的具体服务列表
        List<String> serviceUrlList = CuratorUtils.getChildrenNodes(zkClient, rpcServiceName);
        if (serviceUrlList == null || serviceUrlList.size() == 0) {
            throw new RpcException(RpcErrorMessageEnum.SERVICE_CAN_NOT_BE_FOUND, rpcServiceName);
        }
        // 负载均衡具体选择哪个服务
        String targetServiceUrl = loadBalance.selectServiceAddress(serviceUrlList, request);
        log.info("Successfully found the service address:[{}]", targetServiceUrl);

        // 构造具体的服务地址并返回
        String[] socketAddressArray = targetServiceUrl.split(":");
        String host = socketAddressArray[0];
        int port = Integer.parseInt(socketAddressArray[1]);
        return new InetSocketAddress(host, port);
    }
}
